#include <windows.h>
#include <math.h>

#include <gl\glut.h>
#include <SDL/SDL.h>

#include "bitmap.h"

#define MAX 10

char className[] = {"application_tank_game"};
char wndCaption[] = {"application_game_tank"};

LRESULT CALLBACK WndMsgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

BOOL keys[256];

struct _player
{
	float x;
	float y;
	float angle;
} player;

typedef struct
{
	float posx; /* pozycja */
	float posy;
	
	float velx; /* predkosc */
	float vely;

	float forx; /* sily */
	float fory;

	float mass;
} Object;

Object shell[MAX];
unsigned shoot = 0;
float tmpx, tmpy, tmpa;


void StartEngine();
void DoEngine();

int screen_width = 800;
int screen_height = 600;
int screen_bits = 16;
int screen_frequency = 75;
BOOL full_screen = FALSE;

HGLRC hRC;
HDC hDC;

void SetupPixelFormat(HDC hDC);
void StartWGL(HWND hWnd);
void StopWGL();
void SetFullScreen();
void SetupRC();
void ChangeSize(int width, int height);
void Render();
void renderShell();
void WaitFrame(int fps);

void startupTexture();
void freeTexture();

GLUquadricObj *quadric;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	WNDCLASSEX wc;
	HWND hWnd;
	MSG Msg;
	int style;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndMsgProc;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hIconSm = wc.hIcon;
	wc.lpszClassName = className;
	wc.lpszMenuName = NULL;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	RegisterClassEx(&wc);

	if (full_screen)
	{
		SetFullScreen();
		style = WS_POPUP;
	}
	else
	{
		style = WS_OVERLAPPEDWINDOW;
	}

	hWnd = CreateWindowEx(0, className, wndCaption, style, 0, 0, screen_width, screen_height, NULL, NULL, hInstance, NULL);
	ShowWindow(hWnd, nShowCmd);

	while (1)
	{
		if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE))
		{
			if (Msg.message == WM_QUIT)
			{
				break;
			}

			TranslateMessage(&Msg);
			DispatchMessage(&Msg);
		}
		else
		{
			DoEngine();
			Render();
			SwapBuffers(hDC);
		}
	}

	return 0;
}



LRESULT CALLBACK WndMsgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	switch (Msg)
	{

	case WM_CREATE:
		StartWGL(hWnd);
		SetupRC();
		StartEngine();
		break;

	case WM_DESTROY:
		freeTexture();
		StopWGL();
		PostQuitMessage(0);
		gluDeleteQuadric(quadric);
		break;

	case WM_SIZE:
		{
			int width = LOWORD(lParam);
			int height = HIWORD(lParam);
			ChangeSize(width, height);
		}
		break;

	case WM_KEYDOWN:
		{
			keys[wParam] = TRUE;
		}
		break;

	case WM_KEYUP:
		{
			keys[wParam] = FALSE;
		}
		break;
	}

	return DefWindowProc(hWnd, Msg, wParam, lParam);
}



void SetupPixelFormat(HDC hDC)
{
	int pixelFormat;
	static PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof(PIXELFORMATDESCRIPTOR),
			1,
			PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
			PFD_TYPE_RGBA,
			32,
			0, 0, 0, 0, 0, 0,
			0,
			0,
			0,
			0, 0, 0, 0,
			16,
			0,
			0,
			PFD_MAIN_PLANE,
			0,
			0, 0, 0
	};

	pixelFormat = ChoosePixelFormat(hDC, &pfd);
	SetPixelFormat(hDC, pixelFormat, &pfd);
}



void StartWGL(HWND hWnd)
{
	hDC = GetDC(hWnd);
	SetupPixelFormat(hDC);
	hRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC, hRC);
}



void StopWGL()
{
	wglMakeCurrent(hDC, NULL);
	wglDeleteContext(hRC);
}



void SetFullScreen()
{
	DEVMODE screen = {0};

	screen.dmSize = sizeof(DEVMODE);
	screen.dmPelsWidth = screen_width;
	screen.dmPelsHeight = screen_height;
	screen.dmBitsPerPel = screen_bits;
	screen.dmDisplayFrequency = screen_frequency;
	screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;

	ChangeDisplaySettings(&screen, CDS_FULLSCREEN);
}

void StartEngine()
{
	player.x = 0.0f;
	player.y = 0.0f;
	player.angle = 0.0f;
}

void WaitFrame(int fps)
{
	static int next_tick = 0;
	int this_tick;
	 
	this_tick = SDL_GetTicks();
	if (this_tick < next_tick)
		SDL_Delay(next_tick-this_tick);
	next_tick = SDL_GetTicks() + (1000/fps);

	return;
}

void DoEngine()
{
	if (keys[VK_LEFT] || keys['A'])
		player.angle += 0.0005f;
	
	if (keys[VK_RIGHT] || keys['D'])
		player.angle -= 0.0005f;

	if (keys[VK_UP] || keys['W'])
	{
		player.x -= (float)cos(player.angle) * 0.005f;
		player.y -= (float)sin(player.angle) * 0.005f;
	}

	if (keys[VK_DOWN] || keys['S'])
	{
		player.x += (float)cos(player.angle) * 0.005f;
		player.y += (float)sin(player.angle) * 0.005f;
	}
	if (keys[VK_SPACE])
	{
		shoot = 1;
		tmpx = player.x;
		tmpy = player.y;
		tmpa = player.angle;
	}

	if(shoot){
		static const float resistance = 0.999f; 
		static const float wsp_sily = 0.000002f;

		
		shell[0].forx = 0.0005f;
		shell[0].fory = 0.0005f;

		//shell[0].fory -= shell[0].mass * 9.81f; 
	
		
		shell[0].velx += shell[0].forx * wsp_sily / shell[0].mass;
		shell[0].vely += shell[0].fory * wsp_sily / shell[0].mass;

		
		shell[0].velx *= 1.0f-resistance;
		shell[0].vely *= 1.0f-resistance;
	
		
		//shell[0].posx += shell[0].velx;
		//shell[0].posy += shell[0].vely;
		shell[0].posy += 0.0005f;
		shell[0].posx += 0.0005f;
		}
}


GLuint texture[10];
bitmapFile bitmap[10];



void startupTexture()
{
	loadBitmapFromFile("textures.bmp", &bitmap[0]);

	glGenTextures(1, &texture[0]);
	glBindTexture(GL_TEXTURE_2D, texture[0]);

	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmap[0].width, bitmap[0].height, GL_RGB, GL_UNSIGNED_BYTE, bitmap[0].data);


	/*Floor*/
	loadBitmapFromFile("floor.bmp", &bitmap[1]);

	glGenTextures(1, &texture[1]);
	glBindTexture(GL_TEXTURE_2D, texture[1]);

	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmap[1].width, bitmap[1].height, GL_RGB, GL_UNSIGNED_BYTE, bitmap[1].data);

	//downRobot
	loadBitmapFromFile("rudy.bmp", &bitmap[3]);

	glGenTextures(1, &texture[3]);
	glBindTexture(GL_TEXTURE_2D, texture[3]);

	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmap[3].width, bitmap[3].height, GL_RGB, GL_UNSIGNED_BYTE, bitmap[3].data);

	//backRobot
	loadBitmapFromFile("back.bmp", &bitmap[4]);

	glGenTextures(1, &texture[4]);
	glBindTexture(GL_TEXTURE_2D, texture[4]);

	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmap[4].width, bitmap[4].height, GL_RGB, GL_UNSIGNED_BYTE, bitmap[4].data);

	//bblackRobot
	loadBitmapFromFile("black.bmp", &bitmap[5]);

	glGenTextures(1, &texture[5]);
	glBindTexture(GL_TEXTURE_2D, texture[5]);

	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, bitmap[5].width, bitmap[5].height, GL_RGB, GL_UNSIGNED_BYTE, bitmap[5].data);
	

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	glBindTexture(GL_TEXTURE_2D, texture[0]);
}



void freeTexture()
{
	freeBitmapFile(bitmap);
	glDeleteTextures(1, texture);
}



void SetupRC()
{
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);

	startupTexture();

	quadric = gluNewQuadric();
	gluQuadricDrawStyle(quadric, GLU_FILL);
	gluQuadricNormals(quadric, GLU_SMOOTH);
	gluQuadricOrientation(quadric, GLU_OUTSIDE);
}



void ChangeSize(int width, int height)
{
	glViewport(0, 0, width, height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	gluPerspective(60.0f, (float)width/(float)height, 1.0f, 1000.0f);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void renderFloor(){
	
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glBegin(GL_QUADS);
		
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-10.0f, -10.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(10.0f, -10.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(10.0f, 10.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-10.0f, 10.0f, -1.0f);
		
	glEnd();
	
}

void renderCube(unsigned x, unsigned y)
{
	glBindTexture(GL_TEXTURE_2D, texture[y]);
	glBegin(GL_QUADS);
		
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);
	
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);
		
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);

		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);

		glEnd();
	glBindTexture(GL_TEXTURE_2D, texture[x]);
	glBegin(GL_QUADS);

		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1.0f, 1.0f, 1.0f);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1.0f, 1.0f, -1.0f);
		
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);
		
	glEnd();
}

void renderCylinder()
{
	glPushMatrix();
		glBindTexture(GL_TEXTURE_2D, texture[5]);
		glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
		gluCylinder(quadric, 0.2f, 0.2f, 4.0f, 10, 1);
	glPopMatrix();
}

void renderRobot(){
	renderCube(3, 4);

	glPushMatrix();
		glTranslatef(0.0f, 0.0f, 1.5f);
		glScalef(0.5f, 0.5f, 0.5f);
		renderCube(0, 0);
		renderCylinder();
	glPopMatrix();

}

void renderShell()
{
	int i;
	
	glPushMatrix();	
	for (i = 0; i < 6; i++)
	{
		glBegin(GL_QUADS);
			glVertex3f(-0.1f, 0.1f, 0.1f);
			glVertex3f(-0.1f, -0.1f, 0.1f);
			glVertex3f(0.1f, -0.1f, 0.1f);
			glVertex3f(0.1f, 0.1f, 0.1f);
		glEnd();

		if (i%2 == 0)
			glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		else
			glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
		
	}	
	glPopMatrix();
}

void Render()
{
	//WaitFrame(60);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
	
	glLoadIdentity();
	gluLookAt(7.0f+player.x, 1.5f+player.y, 2.0f, player.x, player.y, 0.0f, 0.0f, 0.0f, 1.0f);

	//SHELL
	glPushMatrix();
		glColor3f(0.8f, 0.8f, 0.0f);
		if(!shoot){
			glTranslatef(player.x, player.y, 0.0f);
			glRotatef(player.angle*180.0f/3.14f, 0.0f, 0.0f, 1.0f);}
		else{
			glTranslatef(tmpx+shell[0].posx, tmpy+shell[0].posy, 0.0f);
			glRotatef(tmpa*180.0f/3.14f, 0.0f, 0.0f, 1.0f);
		}
		glTranslatef(-2.0f, 0.0f, 1.5f);
		renderShell();
	glPopMatrix();

	//ROBOT
	glColor3f(1.0f, 1.0f, 1.0f);
	glPushMatrix();
		
		glTranslatef(player.x, player.y, 0.0f);
		glRotatef(player.angle*180.0f/3.14f, 0.0f, 0.0f, 1.0f);
		renderRobot();
	
	glPopMatrix();

	renderFloor();
	
}